#Send Prowl alerts natively using PowerShell
function Send-ProwlNotification 
{
	<#
        .Synopsis
            Sends a Prowl notification.  Prowl is a service that provides mobile 
			notifications.
        .Description
            Sends a Prowl Notification using one or multiple API Keys. Syntacticly 
			close to the official perl script, but does things in a more 
			"PowerShell way", accepting pipeline input and returning objects.
        .Parameter Apikey
            API Key, or an array of keys associated with one or multiple Prowl 
			accounts.  If omitted, will look for the key in a variable called 
			$ProwlApiKey in the global scope.
        .Parameter ApikeyFile
            File containing API keys, one per line.  Lines starting with a hash 
			mark '#' are considered comments and ignored. Helpful if you want to
			have a predefined list of APIKeys that will receive a given set of
			alerts.
        .Parameter Priority
            Urgency of the alert. Valid values are -2 (least urgent) to 2 (most 
			urgent).
        .Parameter Url
            URL to attach to the notification.  Can be used to send a link to a
			web page, or can cause an application redirect on an iOS device.
        .Parameter Application
            The name of your application or the application generating the event.
        .Parameter Event
            The name of the event or subject of the notification.
        .Parameter Description
            A description of the event, generally terse. Can also be called with 
			-Notification to retain syntax compatibility with the official perl 
			script.
        .Example
            Send-ProwlNotification "This is my notification"
            
            Sends "This is my notification" to API key specified in the global 
			variable $ProwlApiKey, normally specified in the users powershell 
			profile ($Profile).
        .Example
            Send-ProwlNotification -apikey <40 digit api key> -description "This is my notification"
			
			Sends a Prowl notification "This is my notification" with normal priority.
        .Example
            if (-not Test-Connection 192.168.1.10 -quiet) { Send-ProwlNotification "System offline" }
            
            Uses test connection to see if an IP is online.  If it fails the connectivity test,
            an alert is pushed to the API Key specified in $ProwlApiKey.
        .Example
            Send-ProwlNotification -Apikeyfile "c:\users\joeuser\apikeyfile.txt" `
            -Description "this is the notification description" `
            -Priority 2 -Event "Test"
            
			Sends a high priority/emergency alert containing the text "this is
			the notification description" to every API Key in apikeyfile.txt
            
        .Notes
            NAME:      Send-ProwlNotification
            VERSION:   1.0
            AUTHOR:    Alex McFarland
            LASTEDIT:  09/17/2011
		.Link
			http://powerprowl.codeplex.com
            
    #Requires -Version 2.0
    #>

	[CmdletBinding(DefaultParameterSetName = "Key")]
	param(
        [Parameter(
            ParameterSetName = "Key",
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true,
            HelpMessage = "API Key to which notifications are sent. Each is a 40-byte hexadecimal string.")]
        [ValidateLength(40,40)]
        [String[]] $ApiKey = $ProwlApiKey,
    
        [Parameter(
            ParameterSetName = "KeyFile",
            ValueFromPipelineByPropertyName = $true,
            HelpMessage = "File Containing Prowl API keys")]
        [ValidateScript({ Test-Path $_ })] 
        [System.IO.FileInfo] $ApiKeyFile, 
    
        [Parameter(
            ValueFromPipelineByPropertyName = $true,
            HelpMessage = "An integer value ranging [-2, 2]. -2 is low, 2 is an emergency")]
        [ValidateRange(-2,2)]
        [Int] $Priority = 0,
    
        [Parameter(
            ValueFromPipelineByPropertyName=$true,
            HelpMessage = "The URL which should be attached to the notification.")]
        [String] $Url,
    
        [Parameter(
            ValueFromPipelineByPropertyName=$true,
            HelpMessage = "The name of your application or the application generating the event.")]
        [String] $Application = "PowerProwl",
    
        [Parameter(
            ValueFromPipelineByPropertyName=$true,
            HelpMessage = "The name of the event or subject of the notification.")]
        [String] $Event = "PowerShell Notification",
    
        [Parameter(
            ValueFromPipelineByPropertyName = $true,
            Position = 0,
            Mandatory = $true, 
            HelpMessage = "A description of the event, generally terse.")]
        [Alias("Notification")]
        [String] $Description = "PowerShell Notification"
    )
        
    Begin
    {
        # Configure Location of Prowl's API
        $ProwlApiUrl = "https://api.prowlapp.com/publicapi/add"

        # set $OFS to make it easy to turn an array of strings 
        # to a comma seperated list.
        $OFS = "," 
        Write-Verbose "Using paramter set: $($PsCmdlet.ParameterSetName)" 
	}

	Process 
	{
		if ($PsCmdlet.ParameterSetName -eq "None")
		{
			# in the event that no API Key was provided
			# on the command line, check for a global variable
			if ($Global:ProwlApiKey)
			{
				$ApiKey = $Global:ProwlApiKey
			}
			else
			{
				throw "No API Key supplied, and global `$ProwlApiKey global variable not defined."
			}
		}
		if ($ApiKeyFile) 
		{
			# if an ApiKeyFile was defined, read in all keys as 
			# comma seperated list.  This works because $OFS = ","
			# as the data is read in, it's filtered using where-object
			# so that lines tarted with a #mark are ignored.
			[String] $ApiKey = Get-Content $ApiKeyfile | 
			    Where-Object { ($_ -notmatch "^#" ) -and ( $_ -match "\w" ) }
		}
		else
		{
			# if no ApiKeyfile was defined, convert the ApiKey String array
			# to a variable with comma seperated values.
			# works because $OFS = ","
			[String] $ApiKey = $ApiKey
		}

		# create an empty NameValueCollection to hold the postdata.
		$Notification = new-object System.Collections.Specialized.NameValueCollection

		# Add paramers to the NameValueCollection
		$Notification.Add('apikey',$ApiKey)
		$Notification.Add('priority',$Priority)
		$Notification.Add('application',$Application)
		$Notification.Add('event',$Event)
		$Notification.Add('description',$Description)

		if ($Url) 
		{
			$Notification.Add('url',$Url)
		} 

		Write-Verbose "Preparing to send the following payload to Prowl: "
		foreach ($Key in $Notification) {
			Write-Verbose "$Key = $($Notification[$Key])"
		}

		# create a new web client object
		$WebClient = New-Object Net.Webclient

		# post the data to Prowl's API
		Write-Verbose "Attempting to send notification to $ProwlApiUrl"
		try 
		{
			$Response = $WebClient.UploadValues($ProwlApiUrl,$Notification)
			if ($Response)
			{
				Write-Verbose "HTTP POST successful"
				$StatusCode = $Response.StatusCode -as [int]
				$PostSuccess = $true
			}
			else
			{
				$PostSucces = $false
			} 
		} 
		catch 
		{
			#throw "Error sending notification" 
			Write-Verbose "HTTP POST failed"
			Write-Verbose $_.Exception.Message
			$PostSuccess = $false
			$StatusCode = $_.Exception.InnerException.Response.StatusCode -as [Int]
		}

		if ($PostSuccess)
		{
			# webclient returns binary data, so
			# it must be converted to UTF-8
			$Encode = New-Object System.Text.UTF8Encoding
			[xml]$XmlReturn = $Encode.GetString($Response)

			# parse the XML returned from Prowl
			if ($XmlReturn.Prowl.Success) 
			{
				Write-Verbose "Notification Sent"
				$StatusCode = $XmlReturn.Prowl.Success.Code
				$Remaining = $XmlReturn.Prowl.Success.Remaining
				$UnixDate = [Int] $XmlReturn.Prowl.Success.ResetDate
				$ResetDate = [Timezone]::CurrentTimeZone.ToLocalTime(([Datetime]'1/1/1970').AddSeconds($UnixDate)) 
				$Success = $true
			}

			# this section will probably never be called, but is left for the sake
			# of completeness.
			elseif ($XmlReturn.Prowl.Error) 
			{
				Write-Verbose "Errorcode: $XmlReturn.Prowl.Error.Code"
				Write-Verbose "Error Message: $XmlReturn.Prowl.Error.Get_InnerText()"
				$StatusCode = $XmlReturn.Prowl.Error.Code
				$StatusMessage = $XmlReturn.Prowl.Error.Get_InnerText()
				$Success = $false
			} 
		}
		else
		{
			# set static values in the event of an error
			$Remaining = 0
			$ResetDate = [DateTime] "1/1/1970"
			$Success = $false
		}
		# associate text with the error codes.  
		switch ($StatusCode) 
		{
			200 { $StatusMessage = "Notification sent." }
			400 { $StatusMessage = "Bad request. Invalid parameters." }
			401 { $StatusMessage = "Not authorized. Invalid API key." }
			406 { $StatusMessage = "API limit exceeded." }
			409 { $StatusMessage = "Not approved." }
			500 { $StatusMessage = "Internal server error." }
			default { $StatusMessage = "Unknown status or connection failure." }
		} 
        
		# return an error if sending the notification failed.
		# return code can be silent by setting the error action preference.
		if (-not $Success)
		{
            Write-Error $StatusMessage
		}
		
		# create a custom object to hold return values.
		$ProwlNotificationStatus = New-Object -TypeName PSObject -Property @{
			Details = $StatusMessage
			Success = $Success
			APIKey = $ApiKey -split ","
			Remaining = $Remaining
			ResetDate = $ResetDate
		}

		# name the custom object type
		$ProwlNotificationStatus.PSObject.TypeNames.Insert(0,'ProwlNotification.Status')

		# output the status to the pipeline
		$ProwlNotificationStatus
	}
}